



> 'NSUndoManager'  可以给程序添加撤销功能

> 当添加、删除对象时,撤销管理器跟踪这些撤销动作产生的消息。

> 而撤销时,撤销管理器也将跟踪redo这些动作产生的消息。

> 撤销机制使用 'NSInvocation' 对象的两个栈。



> 'NSInvocation'

> 将消息(包括selector/receiver和所有的参数)包装成一个对象能耐带来很多便利
> NSInvocation就是这样一个对象

> NSInvocation最佳应用 场合就是消息转发。当对象收到它不理解的消息时,在抛出异常前,消息转发系统将检查对象是否实现了下面的方法 - (void)forwardInvocation:(NSInvocation *)invocation;
> 加入对象有这样的方法，发送的消息就被打包成一个NSInvocation对象，并调用forwardInvocation:


>'NSUndoManager'的工作机制
> 比如数据的每次修改完成，控制器将添加一个invocation到可以撤销编辑动作的undo栈(先进后出)中 "编辑动作被逆序添加到undo栈"

> 举例:  (1)插入一个新的记录 (2)名字从 a 改成 b  (3)raise从0改到20   undo的动作见图片 "undo栈.png"
>    如果用户选择undo菜单，就从undo中选择第一个invocation并调用它  见图片 "2.修改后的undo站,png"



> '键值观察'

> 对象的keyPath上发生变化将会通知观察者, options 定义了将要变化的内容，例如新值和旧值; context: 指向需要与其他信息一起发送数据的指针;change:可能是包含新值和旧值的字典
> 谨慎使用context指针

> - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context




> '补充知识：window和undo管理器'

> 视图可以添加编辑操作到undo管理器 。 视图怎么知道用的哪个管理器呢？ 它会询问delegate
> 如 NSTextView - (nullable NSUndoManager *)undoManagerForTextView:(NSTextView *)view;
> 然后查询窗口.NSWindow有个属性
    @property (nullable, readonly, strong) NSUndoManager *undoManager;
> 通过此方法,窗口的delegate可以返回undoManager
    - (nullable NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;

> undo/redo菜单可以反映key window的undo管理器的状态

> "keyWindow"被很多用户成为活动窗口, 因为它能获得键盘的状态




















